/*
 *
 * only support for "exact division"
 *
 *
 *
 */


#include "aceMesh_runtime_c.h"
#include "slave.h"
#include "float.h"
#include "sbmd.h"
#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "3d7p_aceMesh.h"
#include "task.h"
#include "ldm_malloc.h"
//__thread_local volatile unsigned long get_reply,put_reply;
__thread_local volatile unsigned long get_reply,get_reply1,get_reply2;
__thread_local volatile unsigned long put_reply;
//kernel region
__thread_local_fix double slave[2][BLKX][BLKY][BLKZ];
//__thread_local_fix double A_rhs[BLKX][BLKY][BLKZ];
//up and down shade region
__thread_local_fix double A_us[BLKY][BLKZ];
__thread_local_fix double A_ds[BLKY][BLKZ];
//left and right shade  region
__thread_local_fix double A_ls[BLKX][BLKZ];
__thread_local_fix double A_rs[BLKX][BLKZ];
//front and back shade region
__thread_local_fix double A_fs[BLKX][BLKY];
__thread_local_fix double A_bs[BLKX][BLKY];
//Record the logical information of the last time executed task.
extern double A[2][SIZEX][SIZEY][SIZEZ];

__thread_local_fix int amt_flag = 0;


#define alpha_d 0.0876
#define beta_d  0.0765

#ifdef _PROFILING
__thread_local int core_id=0;
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start,dma_end;
__thread_local volatile unsigned long comp_st,comp_et;
__thread_local volatile unsigned long dma_et,dma_st,dma_et_,dma_st_; 
extern unsigned long slave_dma_count[64];
extern unsigned long slave_comp_count[64];
extern unsigned long slave_comp_dma_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

void stencil_core(targs* args)
{  
   //0. assumptions       
   //data region: [starti-1,endi] [startj-1,endj] [startk-1,endk]  
   //compute region: [starti, endi) [startj,endj) [startk,endk)
  int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
  int len, bsize, stride;   
  int i_region,j_region,k_region;
  int ii,jj,kk;
  int fi,fj;
  int lk;
  int i_i,j_j,k_k;
  int tileno[3];
  int varno[2];
  int reuse[2];
  int lhs_no,rhs_no;
  int core_dma_num = 0;
#ifdef _PROFILING
  core_id = athread_get_id(-1);
  start=rtc_();
#endif
  s_iter = args->iter;
  dest = (s_iter) % 2 ;
  src = 1 - dest;
  s_starti = args->starti;
  s_endi = args->endi;
  s_startj = args->startj;
  s_endj = args->endj;
  s_startk = 1;
  s_endk = args->k_N-1;
  i_region = s_endi-s_starti-1;
  j_region = s_endj-s_startj-1;
  k_region = s_endk-s_startk-1;   
  i_i = s_endi-s_starti-2;
  j_j = s_endj-s_startj-2;
  k_k = s_endk-s_startk-2;
  if(amt_flag == 0)  
  {
    ldm_set_datatiles(2);//total ldm array
    amt_flag = 1;
  }
  tileno[0]=s_starti/BLKX;
  tileno[1]=s_startj/BLKY;
  tileno[2]=s_startk/BLKZ;
  slave_ldm_postreq_tile(s_iter, &A[dest][0][0][0], tileno, OUT);//
  slave_ldm_postreq_tile(s_iter, &A[src][0][0][0], tileno, IN);
  slave_ldm_getrep(2,varno,reuse);
  lhs_no = varno[0];
  rhs_no = varno[1];
#ifdef _PROFILING
  dma_st=rtc_();
#endif
  //1 update core data
  get_reply=0;
  if(rhs_no <0)
    printf("Array assignment failed\n");
  else if(reuse[1]==0){
    bsize=(BLKZ)*8;
    stride=(SIZEZ-(BLKZ))*8;  //stride: from blocktail to blockhead 
    len= bsize*(BLKY);
    for(i = s_starti; i < s_endi; i++)
       athread_get(PE_MODE,&A[src][i][s_startj][s_startk]
                 , &slave[rhs_no][i-s_starti][0][0]
                 , len, &get_reply
                 , 0, stride, bsize);
    core_dma_num+=(s_endi-s_starti);
 }
  //2 update front and back shade data
  get_reply1 = 0;
  stride = (SIZEZ-1)*8;
  len = (BLKY)*8;
  bsize = 8;
  for(i = s_starti; i < s_endi; ++i )
  {
     athread_get(PE_MODE,&A[src][i][s_startj][s_startk-1]
                   , &A_fs[i-s_starti][0]
                   , len,&get_reply1
                   , 0, stride, bsize);
     athread_get(PE_MODE,&A[src][i][s_startj][s_endk]
                   , &A_bs[i-s_starti][0]
                   , len,&get_reply1
                   , 0, stride, bsize); 
  }
 //3.wait for core data finished 
  while(get_reply!=(core_dma_num));
 
  get_reply2 = 0;
  //4.1 update up and down shade data
  stride = (SIZEZ-(BLKZ))*8;
  len = (BLKY)*(BLKZ)*8;
  bsize = (BLKZ)*8;
  athread_get(PE_MODE,&A[src][s_starti-1][s_startj][s_startk]
                 , &A_us[0][0]
                 , len, &get_reply2
                 , 0, stride, bsize);
  athread_get(PE_MODE,&A[src][s_endi][s_startj][s_startk]
                 , &A_ds[0][0]
                 , len, &get_reply2
                 , 0, stride, bsize);
  //4.2 update left and right shade data
  stride = ((SIZEY-1)*SIZEZ+(SIZEZ-(BLKZ)))*8;
  len = (BLKX)*(BLKZ)*8;
  bsize = (BLKZ)*8;
  athread_get(PE_MODE,&A[src][s_starti][s_startj-1][s_startk]
                   , &A_ls[0][0]
                   , len, &get_reply2
                   , 0, stride, bsize);
  athread_get(PE_MODE,&A[src][s_starti][s_endj][s_startk]
                   , &A_rs[0][0]
                   , len,&get_reply2
                   , 0, stride, bsize);    
  //6 wait for up down left right shade data
  while(get_reply2 != (2+2));
   //8 wait for front back shade data
  while(get_reply1 != ((BLKX)*2));
#ifdef _PROFILING
  dma_et=rtc_();
#endif
#ifdef _PROFILING
  comp_st=rtc_();
#endif
  //5.compute core data
  for(ii = 1; ii < i_region; ii++)
     for(jj = 1; jj < j_region; jj++)
        for(kk = 1; kk < k_region; kk++)
            slave[lhs_no][ii][jj][kk] = alpha_d * slave[rhs_no][ii][jj][kk] + \
                     beta_d * (slave[rhs_no][ii-1][jj][kk] + slave[rhs_no][ii+1][jj][kk] +\
                     slave[rhs_no][ii][jj-1][kk] + slave[rhs_no][ii][jj+1][kk]+\
                     slave[rhs_no][ii][jj][kk-1] + slave[rhs_no][ii][jj][kk+1]);
  //7.compute 4 lines, 4 faces.
  //7.1 compute up and down line of k dimensional change
  for(lk = 1; lk < k_region; ++lk)
  {
    //left face
    slave[lhs_no][0][0][lk] = alpha_d * slave[rhs_no][0][0][lk] + \
                     beta_d * (A_us[0][lk] + slave[rhs_no][1][0][lk] +\
                     A_ls[0][lk] + slave[rhs_no][0][1][lk]+\
                     slave[rhs_no][0][0][lk-1] + slave[rhs_no][0][0][lk+1]);
    slave[lhs_no][i_region][0][lk] = alpha_d * slave[rhs_no][i_region][0][lk]+ \
                     beta_d * (slave[rhs_no][i_i][0][lk] + A_ds[0][lk] +\
                     A_ls[i_region][lk] + slave[rhs_no][i_region][1][lk]+\
                     slave[rhs_no][i_region][0][lk-1]+ slave[rhs_no][i_region][0][lk+1]);
   //right face 
    slave[lhs_no][0][j_region][lk] = alpha_d * slave[rhs_no][0][j_region][lk]+ \
                     beta_d * (A_us[j_region][lk] + slave[rhs_no][1][j_region][lk] +\
                     slave[rhs_no][0][j_j][lk] + A_rs[0][lk]+\
                     slave[rhs_no][0][j_region][lk-1] + slave[rhs_no][0][j_region][lk+1]);
    slave[lhs_no][i_region][j_region][lk] = alpha_d * slave[rhs_no][i_region][j_region][lk] + \
                     beta_d * (slave[rhs_no][i_i][j_region][lk] + A_ds[j_region][lk] +\
                     slave[rhs_no][i_region][j_j][lk] + A_rs[i_region][lk]+\
                     slave[rhs_no][i_region][j_region][lk-1] + slave[rhs_no][i_region][j_region][lk+1]);
  }
  //7.2 compute left and right face 
   for(i = 1; i < i_region; ++i)
    for(k = 1; k < k_region; ++k)
    {
       slave[lhs_no][i][0][k] = alpha_d * slave[rhs_no][i][0][k] + \
                     beta_d * (slave[rhs_no][i-1][0][k] + slave[rhs_no][i+1][0][k] +\
                     A_ls[i][k] + slave[rhs_no][i][1][k]+\
                     slave[rhs_no][i][0][k-1] + slave[rhs_no][i][0][k+1]);
       slave[lhs_no][i][j_region][k] = alpha_d * slave[rhs_no][i][j_region][k] + \
                     beta_d * (slave[rhs_no][i-1][j_region][k] + slave[rhs_no][i+1][j_region][k] +\
                     slave[rhs_no][i][j_j][k] + A_rs[i][k]+\
                     slave[rhs_no][i][j_region][k-1] + slave[rhs_no][i][j_region][k+1]);
    }
   // 7.3 compute up and down face
    for(j = 1; j < j_region; ++j)
    for(k = 1; k < k_region; ++k)
    {
      slave[lhs_no][0][j][k] = alpha_d * slave[rhs_no][0][j][k]+ \
                     beta_d * (A_us[j][k] + slave[rhs_no][1][j][k] +\
                     slave[rhs_no][0][j-1][k] + slave[rhs_no][0][j+1][k]+\
                     slave[rhs_no][0][j][k-1] + slave[rhs_no][0][j][k+1]);
      slave[lhs_no][i_region][j][k] =  alpha_d * slave[rhs_no][i_region][j][k] + \
                     beta_d * (slave[rhs_no][i_i][j][k] + A_ds[j][k] +\
                     slave[rhs_no][i_region][j-1][k] + slave[rhs_no][i_region][j+1][k]+\
                     slave[rhs_no][i_region][j][k-1] + slave[rhs_no][i_region][j][k+1]);
   }
  //9 compute 8 corner point
  //9.1 compute up_left_front_point
  slave[lhs_no][0][0][0] = alpha_d * slave[rhs_no][0][0][0] + \
                   beta_d * (A_us[0][0] + slave[rhs_no][1][0][0] +\
                   A_ls[0][0] + slave[rhs_no][0][1][0]+\
                   A_fs[0][0] + slave[rhs_no][0][0][1]); 
  //9.2 compute up_left_back_point
  slave[lhs_no][0][0][k_region] = alpha_d * slave[rhs_no][0][0][k_region]+ \
                   beta_d * (A_us[0][k_region] +\
                   slave[rhs_no][1][0][k_region] +\
                   A_ls[0][k_region] + slave[rhs_no][0][1][k_region]+\
                   slave[rhs_no][0][0][k_k] + A_bs[0][0]);
  
  //9.3 compute up_right_front_point
  slave[lhs_no][0][j_region][0] = alpha_d * slave[rhs_no][0][j_region][0]+ \
                     beta_d * (A_us[j_region][0] +\
                     slave[rhs_no][1][j_region][0] +\
                     slave[rhs_no][0][j_j][0] + A_rs[0][0]+\
                     A_fs[0][j_region] + slave[rhs_no][0][j_region][1]);
  
  //9.4 compute up_right_back_point
  slave[lhs_no][0][j_region][k_region] = alpha_d * \
                    slave[rhs_no][0][j_region][k_region]+ \
                     beta_d * (A_us[j_region][k_region] + \
                     slave[rhs_no][1][j_region][k_region] + \
                     slave[rhs_no][0][j_j][k_region] + \
                     A_rs[0][k_region]+\
                     slave[rhs_no][0][j_region][k_k] + \
                     A_bs[0][j_region]);  
  //9.5 compute down_left_front_point
   slave[lhs_no][i_region][0][0] =   alpha_d * slave[rhs_no][i_region][0][0] + \
                     beta_d * (slave[rhs_no][i_i][0][0] + A_ds[0][0] +\
                     A_ls[i_region][0] + slave[rhs_no][i_region][1][0]+\
                     A_fs[i_region][0] + slave[rhs_no][i_region][0][1]);  
  //9.6 compute down_left_back_point
   slave[lhs_no][i_region][0][k_region] = alpha_d * \
                     slave[rhs_no][i_region][0][k_region] + \
                     beta_d * (slave[rhs_no][i_i][0][k_region] + \
                     A_ds[0][k_region] +\
                     A_ls[i_region][k_region] +\
                     slave[rhs_no][i_region][1][k_region]+\
                     slave[rhs_no][i_region][0][k_k] +\
                     A_bs[i_region][0]);    
  //9.7 compute down_right_front_point
  slave[lhs_no][i_region][j_region][0] = alpha_d * \
                     slave[rhs_no][i_region][j_region][0] + \
                     beta_d * (slave[rhs_no][i_i][j_region][0] + \
                     A_ds[j_region][0] + \
                     slave[rhs_no][i_region][j_j][0] + \
                     A_rs[i_region][0]+ \
                     A_fs[i_region][j_region] + \
                     slave[rhs_no][i_region][j_region][1]); 
  //9.8  compute down_right_back_point
  slave[lhs_no][i_region][j_region][k_region] = alpha_d * \
                     slave[rhs_no][i_region][j_region][k_region] + \
                     beta_d * (slave[rhs_no][i_i][j_region][k_region] + \
                     A_ds[j_region][k_region] + \
                     slave[rhs_no][i_region][j_j][k_region] + \
                     A_rs[i_region][k_region]+ \
                     slave[rhs_no][i_region][j_region][k_k] + \
                     A_bs[i_region][j_region]); 
 //10 compute 8 line data
  //10.1 compute up and down line of j dimensional  change 
  for(j = 1;j < j_region; ++j)
  {
  //front face
    slave[lhs_no][0][j][0] = alpha_d * slave[rhs_no][0][j][0] + \
                     beta_d * (A_us[j][0] + slave[rhs_no][1][j][0] +\
                     slave[rhs_no][0][j-1][0] + slave[rhs_no][0][j+1][0]+\
                     A_fs[0][j] + slave[rhs_no][0][j][1]);
    slave[lhs_no][i_region][j][0] = alpha_d * slave[rhs_no][i_region][j][0] + \
                     beta_d * (slave[rhs_no][i_i][j][0] + A_ds[j][0] +\
                     slave[rhs_no][i_region][j-1][0] + slave[rhs_no][i_region][j+1][0]+\
                     A_fs[i_region][j] + slave[rhs_no][i_region][j][1]);
  //back face
    slave[lhs_no][0][j][k_region] = alpha_d * slave[rhs_no][0][j][k_region] + \
                     beta_d * (A_us[j][k_region] + slave[rhs_no][1][j][k_region] +\
                     slave[rhs_no][0][j-1][k_region] + slave[rhs_no][0][j+1][k_region]+\
                     slave[rhs_no][0][j][k_k] + A_bs[0][j]);
    slave[lhs_no][i_region][j][k_region] =  alpha_d * slave[rhs_no][i_region][j][k_region] + \
                     beta_d * (slave[rhs_no][i_i][j][k_region] + A_ds[j][k_region] +\
                     slave[rhs_no][i_region][j-1][k_region] + slave[rhs_no][i_region][j+1][k_region]+\
                     slave[rhs_no][i_region][j][k_k] + A_bs[i_region][j]);
  }
  //10.2 compute left and right line of i dimensional change
  for(i = 1;i < i_region; ++i)
  {
   //front face
    slave[lhs_no][i][0][0] = alpha_d * slave[rhs_no][i][0][0] + \
                     beta_d * (slave[rhs_no][i-1][0][0] + slave[rhs_no][i+1][0][0] +\
                     A_ls[i][0] + slave[rhs_no][i][1][0]+\
                     A_fs[i][0] + slave[rhs_no][i][0][1]);
    slave[lhs_no][i][j_region][0] = alpha_d * (slave[rhs_no][i][j_region][0]) + \
                     beta_d * (slave[rhs_no][i-1][j_region][0] + slave[rhs_no][i+1][j_region][0] +\
                     slave[rhs_no][i][j_j][0] + A_rs[i][0]+\
                     A_fs[i][j_region] + slave[rhs_no][i][j_region][1]); 
  //back face 
    slave[lhs_no][i][0][k_region] = alpha_d * slave[rhs_no][i][0][k_region] + \
                     beta_d * (slave[rhs_no][i-1][0][k_region] + slave[rhs_no][i+1][0][k_region] +\
                     A_ls[i][k_region] + slave[rhs_no][i][1][k_region]+\
                     slave[rhs_no][i][0][k_k] + A_bs[i][0]); 
    slave[lhs_no][i][j_region][k_region] = alpha_d * slave[rhs_no][i][j_region][k_region] + \
                     beta_d * (slave[rhs_no][i-1][j_region][k_region] + slave[rhs_no][i+1][j_region][k_region] +\
                     slave[rhs_no][i][j_j][k_region] + A_rs[i][k_region]+\
                     slave[rhs_no][i][j_region][k_k] + A_bs[i][j_region]);
  }
  //11 compute 2 face
  //11.1 compute front and back face 
  for(fi = 1; fi < i_region; ++fi)
    for(fj = 1; fj < j_region; ++fj)
    {
       slave[lhs_no][fi][fj][0] = alpha_d * slave[rhs_no][fi][fj][0] + \
                     beta_d * (slave[rhs_no][fi-1][fj][0] + slave[rhs_no][fi+1][fj][0] +\
                     slave[rhs_no][fi][fj-1][0] + slave[rhs_no][fi][fj+1][0]+\
                     A_fs[fi][fj] + slave[rhs_no][fi][fj][1]); 
       slave[lhs_no][fi][fj][k_region] = alpha_d * slave[rhs_no][fi][fj][k_region] + \
                     beta_d * (slave[rhs_no][fi-1][fj][k_region] + slave[rhs_no][fi+1][fj][k_region] +\
                     slave[rhs_no][fi][fj-1][k_region] + slave[rhs_no][fi][fj+1][k_region]+\
                     slave[rhs_no][fi][fj][k_k] + A_bs[fi][fj]);  
    }
#ifdef _PROFILING
  comp_et=rtc_();
  dma_st_=rtc_();
#endif
  //12 write back
   put_reply=0;
   stride = (SIZEZ-BLKZ)*8;
   bsize = BLKZ*8;
   len = bsize*BLKY;
   for(i =  s_starti; i < s_endi; ++i)
     athread_put(PE_MODE,&slave[lhs_no][i-s_starti][0][0]
                     , &A[dest][i][s_startj][s_startk]
                     ,len,&put_reply,stride,bsize);
   while(put_reply!=(BLKX)); 
#ifdef _PROFILING
  dma_et_=rtc_();
  end=rtc_();
  slave_dma_count[core_id]+=((dma_et_-dma_st_)+(dma_et-dma_st));
  slave_comp_dma_count[core_id]+=(end-start);
  slave_comp_count[core_id]+=(comp_et-comp_st);
#endif
}
